/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.primitives.graph;

import java.awt.Point;

import fr.x9c.cadmium.kernel.Block;
import fr.x9c.cadmium.kernel.CodeRunner;
import fr.x9c.cadmium.kernel.Fail;
import fr.x9c.cadmium.kernel.FalseExit;
import fr.x9c.cadmium.kernel.Primitive;
import fr.x9c.cadmium.kernel.PrimitiveProvider;
import fr.x9c.cadmium.kernel.Value;

/**
 * This class provides the implementation of event-related primitives.
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.0
 * @since 1.0
 */
@PrimitiveProvider
public final class Events {

    /** Value for Caml 'event' type (button pressed). */
    static final int EVENT_BUTTON_DOWN = 0;

    /** Value for Caml 'event' type (button released). */
    static final int EVENT_BUTTON_UP = 1;

    /** Value for Caml 'event' type (key pressed). */
    static final int EVENT_KEY_PRESSED = 2;

    /** Value for Caml 'event' type (mouse moved). */
    static final int EVENT_MOUSE_MOTION = 3;

    /** Value for Caml 'event' parameter (polling). */
    static final int EVENT_POLL = 4;

    /**
     * No instance of this class.
     */
    private Events() {
    } // end empty constructor

    /**
     * Waits until an event (from a given set) occurs.
     * @param ctxt context
     * @param events list of events to wait for
     * @return <i>(mouse_x, mouse_y, is_button_pressed, is_key_pressed, key)</i>
     * @throws Fail.Exception if <i>Graphics</i> module has not been initialized
     */
    @Primitive
    public static Value caml_gr_wait_event(final CodeRunner ctxt,
                                           final Value events)
        throws Fail.Exception, FalseExit {
        final GraphSlot slot = GraphSlot.checkGraph(ctxt);
        Value list = events;

        boolean pressed = false;
        boolean released = false;
        boolean keyPressed = false;
        boolean motion = false;
        boolean poll = false;

        while (list != Value.EMPTY_LIST) {
            final Block listBlock = list.asBlock();
            switch (listBlock.get(0).asLong()) {
            case Events.EVENT_BUTTON_DOWN:
                pressed = true;
                break;
            case Events.EVENT_BUTTON_UP:
                released = true;
                break;
            case Events.EVENT_KEY_PRESSED:
                keyPressed = true;
                break;
            case Events.EVENT_MOUSE_MOTION:
                motion = true;
                break;
            case Events.EVENT_POLL:
                poll = true;
                break;
            } // end switch
            list = listBlock.get(1);
        } // end while

        if (poll) {
            return waitEventPoll(slot);
        } else {
            return slot.waitEvent(ctxt.getContext(),
                                  pressed,
                                  released,
                                  keyPressed,
                                  motion);
        } // end if/else
    } // end method 'caml_gr_wait_event(CodeRunner, Value)'

    /**
     * Polls for event.
     * @param slot graph slot
     * @return <i>(mouse_x, mouse_y, is_button_pressed, is_key_pressed, key)</i>
     */
    private static Value waitEventPoll(final GraphSlot slot) {
        final Point pt = slot.getFrame().getMousePosition();
        final int mouseX;
        final int mouseY;
        if (pt != null) {
            mouseX = pt.x;
            mouseY = pt.y;
        } else {
            mouseX = -1;
            mouseY = -1;
        } // end if/else
        return allocateResult(slot,
                              mouseX,
                              mouseY,
                              slot.isMouseButton(),
                              slot.getKey());
    } // end method 'waitEventPoll(GraphSlot)'

    /**
     * Returns a value of <i>status</i> type from passed data.
     * @param slot graph slot
     * @param mouseX x-component of mouse coordinate
     * @param mouseY y-component of mouse coordinate
     * @param button whether a button is pressed
     * @param key <tt>null</tt> if no key is pressed,
     *            otherwise the character of the pressed key
     * @return a value of <i>status</i> type from passed data
     */
    static Value allocateResult(final GraphSlot slot,
                                final int mouseX,
                                final int mouseY,
                                final boolean button,
                                final Integer key) {
        final Block res = Block.createBlock(5, 0);
        res.set(0, Value.createFromLong(mouseX));
        res.set(1, Value.createFromLong(mouseY == -1
                                        ? -1
                                        : slot.convertY(mouseY)));
        res.set(2, button ? Value.TRUE : Value.FALSE);
        res.set(3, key != null ? Value.TRUE : Value.FALSE);
        res.set(4, key != null
                ? Value.createFromLong(key.intValue())
                : Value.ZERO);
        return Value.createFromBlock(res);
    } // end method 'allocateResult(GraphSlot, int, int, boolean, Integer)'

} // end class 'Events'
